package asteroids.entity;

import org.apache.log4j.Logger;

import asteroids.engine.GameState;
import asteroids.engine.Utils;
import asteroids.engine.ai.Pilot;
import asteroids.entity.equipment.Exhaust;
import asteroids.entity.equipment.PointExhaust;

/**
 * In addition to the base Ship attributes, Playership defines - hardpoints -
 * where from the ship bullets originate - default control dynamics; anything
 * deviating significantly from this scheme (i.e., inertialess ships) will need
 * to be subclassed and override update(GameState).
 * 
 * @author whitecd1
 * 
 */
public class PlayerShip extends Ship
{
    private static Logger logger = Logger.getLogger(Ship.class);

    // ship control
    protected double turnSpeed;
    protected double acceleration;

    private int priorOrders;

    private Utils.PolarCoordinate[] hardPoints;

    private Exhaust exhaust;

    /**
     * Takes a full set of parameters to initialize the ship
     * 
     * @param n
     * @param x
     * @param y
     * @param shipShape
     * @param turnSpeed
     * @param acceleration
     * @param maxEnergy
     * @param integrity
     * @param mass
     */
    public PlayerShip(String n, double x, double y, Utils.PolarCoordinate[] shipShape,
            double turnSpeed, double acceleration, int maxEnergy, double integrity, int mass)
    {
        super(n, x, y, shipShape, GameObject.PLAYER, maxEnergy, integrity, mass);
        rotation = 270; // face up

        // ship control vars
        this.turnSpeed = turnSpeed;
        this.acceleration = acceleration;

        hardPoints = new Utils.PolarCoordinate[1];
        hardPoints[0] = objectShape[0];
        exhaust = new PointExhaust(this);
    }

    /**
     * Initializes the ship, using default values for turn speed, acceleration,
     * and maximum energy.
     * 
     * @param n
     * @param x
     * @param y
     * @param shipShape
     */
    public PlayerShip(String n, double x, double y, Utils.PolarCoordinate[] shipShape)
    {
        this(n, x, y, shipShape, 180.0 / Utils.FRAMES_PER_SECOND, 180.0 / Utils.FRAMES_PER_SECOND
                / Utils.FRAMES_PER_SECOND, 20, 1.0, 5);
    }

    public void setAcceleration(double acc)
    {
        acceleration = acc;
    }

    public void setHardPoints(int[] points)
    {
        hardPoints = new Utils.PolarCoordinate[points.length]; // yay for
                                                               // garbage
                                                               // collection
        for( int i = 0; i < points.length; i++ )
        {
            while (points[i] > objectShape.length - 1)
            {
                points[i] -= objectShape.length;
                logger.warn("Out-of-bounds Hardpoint is being set. Wrapping");
            }
            hardPoints[i] = objectShape[points[i]];
        }
    }

    public Utils.PolarCoordinate[] getHardPoints()
    {
        return hardPoints;
    }

    public void update(GameState state)
    {
        if( isAlive() )
        {
            int orders = pilot.getOrders(state);

            rot_speed = 0;
            if( (orders & Pilot.RIGHT) != 0 )
            {
                rot_speed += turnSpeed;
            }
            if( (orders & Pilot.LEFT) != 0 )
            {
                rot_speed += -turnSpeed;
            }
            if( (orders & Pilot.UP) != 0 )
            {
                // engage thrusters!
                Utils.CartesianCoordinate accVector = Utils.cartesianFromPolar(Utils
                        .createPolarCoordinate(rotation, acceleration));

                x_speed = x_speed + accVector.getXComponent();
                y_speed += accVector.getYComponent();

                // apply friction
                x_speed = x_speed - (x_speed * Utils.MU);
                y_speed = y_speed - (y_speed * Utils.MU);

                // calculate direction and speed for bullets, even though Ship
                // will recalculate TODO - bullets should be able to just get
                // ship's metrics themselves, plus most don't rely on ship's
                // motion anymore. This can be removed, yes?
                Utils.PolarCoordinate velocity = Utils.polarFromCartesian(Utils
                        .createCartesianCoordinate(x_speed, y_speed));
                direction = velocity.getRotation();
                speed = velocity.getRadius();

                // add vroom effects
                exhaust.vroom();
                // for( int i = 0; i < 2; i++ )
                // {
                // Utils.CartesianCoordinate exhaust = Utils.cartesianFromPolar(
                // Utils.createPolarCoordinate(rotation + 180, max_radius * 2 /
                // 3))
                // .shiftBy(x_position, y_position);
                // Icing j = new Icing(exhaust.getXComponent(),
                // exhaust.getYComponent(),
                // (int) (.5 * Utils.FRAMES_PER_SECOND));
                // j.setVelocity(Math.random() * (19.8 /
                // Utils.FRAMES_PER_SECOND) - .15,
                // Math.random() * (19.8 / Utils.FRAMES_PER_SECOND) - .15);
                // j.addVelocity(Utils.cartesianFromPolar(Utils.createPolarCoordinate(
                // rotation + 180, 48.0 / Utils.FRAMES_PER_SECOND)));
                // j.addVelocity(Utils.cartesianFromPolar(Utils.createPolarCoordinate(direction,
                // speed)));
                // icing.add(j);
                // }
            }

            if( (orders & Pilot.FIRE) != 0 )
            {
                weaponEngaged = true; // flag the weapon as firing so that can
                                      // be done when all is updated.
                // super.FIRE(state); // keep track of ammunition
            }
            if( ((orders & Pilot.FIRE) == 0) && ((priorOrders & Pilot.FIRE) != 0) )
            {
                if( weapon != null )
                {
                    weapon.release();
                }
            }
            if( (orders & Pilot.SPECIAL) != 0 )
            {
                if( special != null )
                {
                    special.engage();
                }
            }
            priorOrders = orders;
        }

        // now that orders are set, apply them.
        super.update(state);
    }
}
